﻿using System;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using RotovapPlatform.Domain.Common;
using RotovapPlatform.Domain.Common.Exceptions;
using RotovapPlatform.Domain.Equipment.Device.Valves.Dto;
using RotovapPlatform.Domain.Equipment.Interfaces.Base;

namespace RotovapPlatform.Infrastructure.Equipment.Devices.Valve
{
	/// <summary>
	/// 切换阀驱动实现
	/// </summary>
	public class ValveDrive : IValveBase, IDisposable
	{
		private readonly ILogger<ValveDrive> _logger;
		private readonly string _portName;
		private readonly byte _slaveId;
		private readonly SerialPort _serialPort;
		private readonly object _lockObject = new object();
		private bool _disposed = false;
		private DateTime _lastConnectionAttempt = DateTime.MinValue;
		private const int ConnectionRetryIntervalMs = 5000; // 重连间隔5秒
		private bool _isConnected = false;

		/// <summary>
		/// 构造函数
		/// </summary>
		/// <param name="portName">COM端口名，如"COM5"</param>
		/// <param name="slaveId">从站ID</param>
		/// <param name="name">阀门名称</param>
		/// <param name="logger">日志记录器</param>
		public ValveDrive(
			string portName,
			byte slaveId,
			string name,
			ILogger<ValveDrive> logger)
		{
			_portName = portName ?? throw new ArgumentNullException(nameof(portName));
			_slaveId = slaveId;
			_logger = logger ?? throw new ArgumentNullException(nameof(logger));
			Name = name ?? throw new ArgumentNullException(nameof(name));
			EquipmentId = $"Valve_{portName}_{slaveId}";

			try
			{
				_serialPort = new SerialPort
				{
					PortName = _portName,
					BaudRate = 9600,
					DataBits = 8,
					Parity = Parity.None,
					StopBits = StopBits.One,
					ReadTimeout = 2000,
					WriteTimeout = 2000
				};

				// 初始化时尝试打开串口
				TryOpenSerialPort();
				_logger.LogInformation("切换阀 {Name} 已初始化，使用端口 {PortName}", Name, _portName);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, "初始化切换阀 {Name} 串口 {PortName} 失败", Name, _portName);
				throw;
			}
		}

		/// <inheritdoc/>
		public string Name { get; }

		/// <inheritdoc/>
		public string EquipmentId { get; }

		/// <summary>
		/// 获取设备是否已连接
		/// </summary>
		public bool IsConnected
		{
			get
			{
				lock (_lockObject)
				{
					return _isConnected && _serialPort != null && _serialPort.IsOpen;
				}
			}
		}

		/// <summary>
		/// 尝试打开串口，如果失败则记录异常但不抛出
		/// </summary>
		/// <returns>是否成功打开</returns>
		private bool TryOpenSerialPort()
		{
			if (_serialPort == null)
				return false;

			lock (_lockObject)
			{
				// 检查重连间隔
				if ((DateTime.Now - _lastConnectionAttempt).TotalMilliseconds < ConnectionRetryIntervalMs)
				{
					return _isConnected;
				}

				_lastConnectionAttempt = DateTime.Now;

				try
				{
					if (!_serialPort.IsOpen)
					{
						_serialPort.Open();
						_isConnected = true;
						_logger.LogInformation("切换阀 {Name} 的串口 {PortName} 已成功打开", Name, _portName);
					}
					else
					{
						_isConnected = true;
					}
					return true;
				}
				catch (Exception ex)
				{
					_isConnected = false;
					_logger.LogWarning(ex, "打开切换阀 {Name} 的串口 {PortName} 失败", Name, _portName);
					return false;
				}
			}
		}

		/// <summary>
		/// 确保串口已连接或尝试重连，如果无法连接则抛出异常
		/// </summary>
		private void EnsureConnected()
		{
			if (!IsConnected && !TryOpenSerialPort())
			{
				throw new CommunicationException($"切换阀 {Name} 的串口 {_portName} 未连接且重连失败");
			}
		}

		/// <inheritdoc/>
		public async Task<int> GetCurrentChannelAsync()
		{
			try
			{
				// 确保串口已连接
				EnsureConnected();

				// 构建查询当前通道命令
				byte[] command = BuildQueryCurrentChannelCommand();

				// 预期响应长度 (STX + ADDR + FUNC + 数据(1字节) + 数据(1字节) + ETX + 校验和(2字节))
				int expectedResponseLength = 8;

				// 发送命令并接收响应
				byte[] response = await SendCommandAsync(command, expectedResponseLength);

				// 验证响应
				if (!ValidateResponse(response))
				{
					throw new CommunicationException("接收到无效的查询当前通道响应");
				}

				// 根据示例 CC 00 00 06 00 DD AF 01，第三个字节(索引3)是当前通道
				int currentChannel = response[3];
				_logger.LogDebug("切换阀 {Name} 当前通道: {Channel}", Name, currentChannel);

				return currentChannel;
			}
			catch (Exception ex)
			{
				_isConnected = false; // 通信失败时标记为断开连接
				_logger.LogError(ex, "查询切换阀 {Name} 当前通道失败", Name);
				throw new CommunicationException($"查询当前通道失败: {ex.Message}", ex);
			}
		}

		/// <inheritdoc/>
		public async Task<ValveStatus> GetValveStatusAsync()
		{
			try
			{
				// 获取当前通道
				int currentChannel = await GetCurrentChannelAsync();
				_isConnected = true; // 通信成功则标记为已连接

				// 构建并返回状态对象
				return new ValveStatus
				{
					CurrentChannel = currentChannel,
					IsConnected = true
				};
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, "获取切换阀 {Name} 状态失败", Name);
				return new ValveStatus
				{
					CurrentChannel = -1, // 表示未知状态
					IsConnected = false
				};
			}
		}

		/// <inheritdoc/>
		public async Task<OperationResult> SwitchToChannelAsync(int channel)
		{
			try
			{
				// 参数验证
				if (channel < 0 || channel > 6)  // 阀门通道范围为0-6
				{
					return OperationResult.Fail($"无效的通道值: {channel}，有效范围为0-6");
				}

				// 确保串口已连接
				EnsureConnected();

				// 构建切换通道命令
				byte[] command = BuildSwitchChannelCommand(channel);

				// 预期响应长度 (STX + ADDR + FUNC + 数据(1字节) + 数据(1字节) + ETX + 校验和(2字节))
				int expectedResponseLength = 8;

				// 发送命令并接收响应
				byte[] response = await SendCommandAsync(command, expectedResponseLength);

				// 验证响应
				if (!ValidateResponse(response))
				{
					_isConnected = false; // 无效响应时标记为断开连接
					return OperationResult.Fail("接收到无效的切换通道响应");
				}

				_isConnected = true; // 通信成功则标记为已连接
				_logger.LogInformation("切换阀 {Name} 切换到通道: {Channel} 命令已发送", Name, channel);
				return OperationResult.Ok($"切换阀 {Name} 切换到通道 {channel} 命令已发送");
			}
			catch (Exception ex)
			{
				_isConnected = false; // 通信失败时标记为断开连接
				_logger.LogError(ex, "控制切换阀 {Name} 切换到通道 {Channel} 失败", Name, channel);
				return OperationResult.Fail($"切换到通道失败: {ex.Message}");
			}
		}

		/// <inheritdoc/>
		public async Task<OperationResult> SwitchToChannelAndWaitAsync(int channel, int timeoutMs = 10000)
		{
			try
			{
				// 先获取当前状态，如果已经在目标通道，则直接返回成功
				var currentChannel = await GetCurrentChannelAsync();
				if (currentChannel == channel)
				{
					_logger.LogInformation("切换阀 {Name} 已在通道 {Channel}，无需切换", Name, channel);
					return OperationResult.Ok($"切换阀 {Name} 已在通道 {channel}，无需切换");
				}

				// 发送切换命令
				var switchResult = await SwitchToChannelAsync(channel);
				if (!switchResult.Success)
				{
					return switchResult;
				}

				// 等待切换完成
				return await WaitForSwitchCompletionAsync(channel, timeoutMs);
			}
			catch (Exception ex)
			{
				_isConnected = false; // 通信失败时标记为断开连接
				_logger.LogError(ex, "切换阀 {Name} 切换并等待完成失败", Name);
				return OperationResult.Fail($"切换并等待失败: {ex.Message}");
			}
		}

		/// <summary>
		/// 等待切换完成
		/// </summary>
		/// <param name="targetChannel">目标通道</param>
		/// <param name="timeoutMs">超时时间(毫秒)</param>
		/// <returns>操作结果</returns>
		private async Task<OperationResult> WaitForSwitchCompletionAsync(int targetChannel, int timeoutMs = 10000)
		{
			try
			{
				DateTime startTime = DateTime.Now;
				int pollIntervalMs = 200; // 轮询间隔

				while ((DateTime.Now - startTime).TotalMilliseconds < timeoutMs)
				{
					// 查询当前通道
					var currentChannel = await GetCurrentChannelAsync();

					// 如果当前通道与目标通道一致，则完成
					if (currentChannel == targetChannel)
					{
						_logger.LogInformation("切换阀 {Name} 已成功切换到通道 {Channel}", Name, targetChannel);
						return OperationResult.Ok($"切换阀 {Name} 已成功切换到通道 {targetChannel}");
					}

					// 等待一段时间后再次查询
					await Task.Delay(pollIntervalMs);
				}

				// 超时处理
				_logger.LogWarning("切换阀 {Name} 切换到通道 {Channel} 超时", Name, targetChannel);
				return OperationResult.Fail($"切换阀 {Name} 切换到通道 {targetChannel} 超时");
			}
			catch (Exception ex)
			{
				_isConnected = false; // 通信失败时标记为断开连接
				_logger.LogError(ex, "等待切换阀 {Name} 切换到通道 {Channel} 时发生异常", Name, targetChannel);
				return OperationResult.Fail($"等待切换完成时发生异常: {ex.Message}");
			}
		}

		/// <inheritdoc/>
		public async Task<OperationResult> ResetAsync()
		{
			try
			{
				// 确保串口已连接
				EnsureConnected();

				// 构建复位命令
				byte[] command = BuildResetCommand();

				// 预期响应长度 (STX + ADDR + FUNC + 数据(1字节) + 数据(1字节) + ETX + 校验和(2字节))
				int expectedResponseLength = 8;

				// 发送命令并接收响应
				byte[] response = await SendCommandAsync(command, expectedResponseLength);

				// 验证响应
				if (!ValidateResponse(response))
				{
					_isConnected = false; // 无效响应时标记为断开连接
					return OperationResult.Fail("接收到无效的复位响应");
				}

				_isConnected = true; // 通信成功则标记为已连接
				_logger.LogInformation("正在复位切换阀 {Name}", Name);
				return OperationResult.Ok($"切换阀 {Name} 复位指令已发送");
			}
			catch (Exception ex)
			{
				_isConnected = false; // 通信失败时标记为断开连接
				_logger.LogError(ex, "复位切换阀 {Name} 失败", Name);
				return OperationResult.Fail($"复位切换阀失败: {ex.Message}");
			}
		}

		/// <summary>
		/// 发送命令并接收响应
		/// </summary>
		/// <param name="command">命令字节数组</param>
		/// <param name="expectedResponseLength">期望的响应长度</param>
		/// <returns>响应字节数组</returns>
		private async Task<byte[]> SendCommandAsync(byte[] command, int expectedResponseLength)
		{
			return await Task.Run(() =>
			{
				lock (_lockObject)
				{
					try
					{
						// 确保连接
						if (!_serialPort.IsOpen)
						{
							throw new CommunicationException("串口未打开，无法发送命令");
						}

						// 清空接收缓冲区
						_serialPort.DiscardInBuffer();

						// 发送命令
						_serialPort.Write(command, 0, command.Length);
						_logger.LogDebug("已向切换阀 {Name} 发送命令: {Command}", Name,
							BitConverter.ToString(command).Replace("-", " "));

						// 等待一小段时间确保响应已准备好
						Thread.Sleep(50);

						// 读取响应
						byte[] response = new byte[expectedResponseLength];
						int bytesRead = _serialPort.Read(response, 0, expectedResponseLength);

						if (bytesRead != expectedResponseLength)
						{
							throw new CommunicationException(
								$"接收到的响应长度不足，期望 {expectedResponseLength} 字节，实际收到 {bytesRead} 字节");
						}

						_logger.LogDebug("已从切换阀 {Name} 接收响应: {Response}", Name,
							BitConverter.ToString(response).Replace("-", " "));

						return response;
					}
					catch (TimeoutException)
					{
						throw new CommunicationException("串口通信超时");
					}
					catch (Exception ex) when (!(ex is CommunicationException))
					{
						throw new CommunicationException($"串口通信错误: {ex.Message}", ex);
					}
				}
			});
		}

		/// <summary>
		/// 构建查询当前通道命令
		/// </summary>
		/// <returns>命令字节数组</returns>
		private byte[] BuildQueryCurrentChannelCommand()
		{
			// 示例: CC 00 3E 00 00 DD
			byte[] command = new byte[6];

			command[0] = ValveCommand.STX;                // 帧头 (0xCC)
			command[1] = _slaveId;                        // 从站地址
			command[2] = ValveCommand.QUERY_CURRENT_CHANNEL;  // 功能码 (0x3E)
			command[3] = 0x00;                            // 数据1
			command[4] = 0x00;                            // 数据2
			command[5] = ValveCommand.ETX;                // 帧尾 (0xDD)

			// 计算校验和 (帧头至帧尾的所有字节累加和)
			ushort checksum = CalculateChecksum(command, 0, 6);
			// 根据校验和拆分为高低字节
			byte[] finalCommand = new byte[8];
			Array.Copy(command, finalCommand, 6);
			finalCommand[6] = (byte)(checksum & 0xFF);    // 校验和低字节
			finalCommand[7] = (byte)(checksum >> 8);      // 校验和高字节

			return finalCommand;
		}

		/// <summary>
		/// 构建切换通道命令
		/// </summary>
		/// <param name="channel">目标通道</param>
		/// <returns>命令字节数组</returns>
		private byte[] BuildSwitchChannelCommand(int channel)
		{
			// 示例: CC 00 44 06 00 DD
			byte[] command = new byte[6];

			command[0] = ValveCommand.STX;               // 帧头 (0xCC)
			command[1] = _slaveId;                       // 从站地址
			command[2] = ValveCommand.SWITCH_CHANNEL;    // 功能码 (0x44)
			command[3] = (byte)channel;                  // 目标通道
			command[4] = 0x00;                           // 数据2
			command[5] = ValveCommand.ETX;               // 帧尾 (0xDD)

			// 计算校验和 (帧头至帧尾的所有字节累加和)
			ushort checksum = CalculateChecksum(command, 0, 6);
			// 根据校验和拆分为高低字节
			byte[] finalCommand = new byte[8];
			Array.Copy(command, finalCommand, 6);
			finalCommand[6] = (byte)(checksum & 0xFF);   // 校验和低字节
			finalCommand[7] = (byte)(checksum >> 8);     // 校验和高字节

			return finalCommand;
		}

		/// <summary>
		/// 构建复位命令
		/// </summary>
		/// <returns>命令字节数组</returns>
		private byte[] BuildResetCommand()
		{
			// 示例: CC 00 45 00 00 DD
			byte[] command = new byte[6];

			command[0] = ValveCommand.STX;               // 帧头 (0xCC)
			command[1] = _slaveId;                       // 从站地址
			command[2] = ValveCommand.RESET;             // 功能码 (0x45)
			command[3] = 0x00;                           // 数据1
			command[4] = 0x00;                           // 数据2
			command[5] = ValveCommand.ETX;               // 帧尾 (0xDD)

			// 计算校验和 (帧头至帧尾的所有字节累加和)
			ushort checksum = CalculateChecksum(command, 0, 6);
			// 根据校验和拆分为高低字节
			byte[] finalCommand = new byte[8];
			Array.Copy(command, finalCommand, 6);
			finalCommand[6] = (byte)(checksum & 0xFF);   // 校验和低字节
			finalCommand[7] = (byte)(checksum >> 8);     // 校验和高字节

			return finalCommand;
		}

		/// <summary>
		/// 计算校验和 - 计算所有字节的累加和
		/// </summary>
		/// <param name="data">数据数组</param>
		/// <param name="startIndex">起始索引</param>
		/// <param name="length">计算长度</param>
		/// <returns>校验和</returns>
		private ushort CalculateChecksum(byte[] data, int startIndex, int length)
		{
			ushort checksum = 0;
			for (int i = startIndex; i < startIndex + length; i++)
			{
				checksum += data[i];
			}
			return checksum;
		}

		/// <summary>
		/// 验证响应数据有效性
		/// </summary>
		/// <param name="response">响应数据</param>
		/// <returns>是否有效</returns>
		private bool ValidateResponse(byte[] response)
		{
			// 检查响应长度
			if (response == null || response.Length < 8)
			{
				_logger.LogWarning("响应数据长度不足");
				return false;
			}

			// 检查帧头和帧尾
			if (response[0] != ValveCommand.STX || response[5] != ValveCommand.ETX)
			{
				_logger.LogWarning("响应数据帧头或帧尾错误");
				return false;
			}

			// 检查从站地址
			if (response[1] != _slaveId)
			{
				_logger.LogWarning("响应数据从站地址错误");
				return false;
			}

			// 计算并验证校验和
			ushort calculatedChecksum = CalculateChecksum(response, 0, 6);
			// 从响应中获取低字节和高字节，重构校验和
			ushort receivedChecksum = (ushort)((response[7] << 8) | response[6]);

			if (calculatedChecksum != receivedChecksum)
			{
				_logger.LogWarning("响应数据校验和错误: 计算值={0:X4}, 接收值={1:X4}",
					calculatedChecksum, receivedChecksum);
				return false;
			}

			return true;
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!_disposed)
			{
				if (disposing && _serialPort != null)
				{
					try
					{
						if (_serialPort.IsOpen)
						{
							_serialPort.Close();
							_logger.LogDebug("切换阀 {Name} 的串口 {PortName} 已关闭", Name, _portName);
						}
						_serialPort.Dispose();
					}
					catch (Exception ex)
					{
						_logger.LogError(ex, "关闭切换阀 {Name} 的串口 {PortName} 时发生错误", Name, _portName);
					}
				}
				_disposed = true;
			}
		}

		~ValveDrive()
		{
			Dispose(false);
		}
	}

	/// <summary>
	/// 阀门命令常量
	/// </summary>
	internal static class ValveCommand
	{
		/// <summary>
		/// 帧头
		/// </summary>
		public const byte STX = 0xCC;

		/// <summary>
		/// 帧尾
		/// </summary>
		public const byte ETX = 0xDD;

		/// <summary>
		/// 查询当前通道功能码
		/// </summary>
		public const byte QUERY_CURRENT_CHANNEL = 0x3E;

		/// <summary>
		/// 切换通道功能码
		/// </summary>
		public const byte SWITCH_CHANNEL = 0x44;

		/// <summary>
		/// 复位功能码
		/// </summary>
		public const byte RESET = 0x45;
	}
}